function JSONRPCRequest(endpoint, method, params, successCallback, errorCallback) {
	var id = guid();
	
	new Ajax.Request(endpoint + '/' + method, {
		method: 'post',
		contentType: 'application/json',
		requestHeaders: {Accept: 'application/json'},
		postBody: Object.toJSON({jsonrpc: '2.0', id: id, method: method, params: params}),
		onSuccess: function(request) {
			var response = request.responseJSON;

			if (id == response.id && response.result != null) {
				successCallback(response.result);
			} else {
				errorCallback(response.error);
			}
		}
	});
}

function S4() {
   return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}

function guid() {
   return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}

var JSONRPCProxy = Class.create({

	initialize: function(endpoint) {
		this.endpoint = endpoint;
		this.ready = false;
		this.getSMD(this.parseSMD);
	},
	
	getSMD: function(callback) {
		new Ajax.Request(this.endpoint + '/system.description', {
			method: 'post',
			contentType: 'application/json',
			requestHeaders: {Accept: 'application/json'},
			onSuccess: callback.bind(this)
		});
	},
	
	parseSMD: function(response) {
		smd = response.responseText.evalJSON();
		
		for (var i = 0; i < smd.methods.length; i++) {
			this.addProxyMethod(smd.methods[i], null);
		}
		
		this.ready = true;
	},
	
	addProxyMethod: function(method, params) {
		str = 'this.' + method.name +' = function(params, success, error) {' + 
			'JSONRPCRequest("' + this.endpoint + '", "' + method.name + '", params, success, error);' +
			'}';
			
		eval(str);
	}
});
